package com.qqt.csr.workbench.service;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.qqt.csr.common.enums.StatusEnum;
import com.qqt.csr.common.exception.StatusCode;
import com.qqt.csr.common.utils.CacheKeyUtil;
import com.qqt.csr.common.utils.JsonUtil;
import com.qqt.csr.common.utils.ServiceAssert;
import com.qqt.csr.workbench.convert.SkillSetConfigAssembler;
import com.qqt.csr.workbench.entity.SkillSet;
import com.qqt.csr.workbench.entity.SkillSetConfig;
import com.qqt.csr.workbench.mapper.SkillSetConfigMapper;
import com.qqt.csr.workbench.vo.req.SettingSkillSetConfigReqVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.BatchResult;
import org.redisson.api.RBatch;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class SkillSetConfigService extends ServiceImpl<SkillSetConfigMapper, SkillSetConfig> {
    @Autowired
    private SkillSetService skillSetService;
    @Autowired
    private RedissonClient redissonClient;

    /**
     * 设置技能组配置
     *
     * @param req
     */
    public void addOrUpdate(SettingSkillSetConfigReqVO req, String tenantId) {
        SkillSet skillSet = skillSetService.queryById(req.getSkillSetId(), tenantId);
        ServiceAssert.notNull(skillSet, StatusCode.Common.NOT_EXIST.getCode(), "当前技能组不存在，配置失败");

        SkillSetConfig skillSetConfig = SkillSetConfigAssembler.INSTANCE.toSkillSetConfig(req);

        if (skillSetConfig.getVisitorDisconnTimeoutEnable().equals(StatusEnum.ENABLE.getValue())) {
            // 开启访客超时设置
            ServiceAssert.notNull(skillSetConfig.getReplyVisitorTimeout(), StatusCode.Common.NULL_ERROR.getCode(), "访客超时回复触发提醒未设置，配置失败");
            ServiceAssert.isTrue(StringUtils.isNotBlank(skillSetConfig.getReplyVisitorTimeoutTip()), StatusCode.Common.NULL_ERROR.getCode(), "访客超时回复触发提醒内容未设置，配置失败");
            ServiceAssert.notNull(skillSetConfig.getVisitorDisconnTimeout(), StatusCode.Common.NULL_ERROR.getCode(), "访客超时断开时间未设置，配置失败");
            ServiceAssert.isTrue(skillSetConfig.getVisitorDisconnTimeout().compareTo(skillSetConfig.getReplyVisitorTimeout()) >= 0, StatusCode.Common.ILLEGAL_PARAM_ERROR.getCode(), "访客超时断开时间必须大于等于访客超时回复触发提醒，配置失败");
            ServiceAssert.isTrue(skillSetConfig.getVisitorDisconnTimeout().compareTo(3600) <= 0, StatusCode.Common.ILLEGAL_PARAM_ERROR.getCode(), "访客超时断开时间最大60分钟，配置失败");
        } else {
            // 未开启访客超时设置
            skillSetConfig.setReplyVisitorTimeout(-1);
            skillSetConfig.setReplyVisitorTimeoutTip(StringUtils.EMPTY);
            skillSetConfig.setVisitorDisconnTimeout(-1);
        }

        if (skillSetConfig.getCsDisconnTimeoutEnable().equals(StatusEnum.ENABLE.getValue())) {
            // 开启客服超时设置
            ServiceAssert.notNull(skillSetConfig.getReplyCsTimeout(), StatusCode.Common.NULL_ERROR.getCode(), "客服超时回复触发提醒未设置，配置失败");
            ServiceAssert.isTrue(StringUtils.isNotBlank(skillSetConfig.getReplyCsTimeoutTip()), StatusCode.Common.NULL_ERROR.getCode(), "客服超时回复触发提醒内容未设置，配置失败");
            ServiceAssert.notNull(skillSetConfig.getCsDisconnTimeout(), StatusCode.Common.NULL_ERROR.getCode(), "客服超时断开时间未设置，配置失败");
            ServiceAssert.isTrue(skillSetConfig.getCsDisconnTimeout().compareTo(skillSetConfig.getReplyCsTimeout()) >= 0, StatusCode.Common.ILLEGAL_PARAM_ERROR.getCode(), "客服超时断开时间必须大于等于客服超时回复触发提醒，配置失败");
            ServiceAssert.isTrue(skillSetConfig.getCsDisconnTimeout().compareTo(3600) <= 0, StatusCode.Common.ILLEGAL_PARAM_ERROR.getCode(), "客服超时断开时间最大60分钟，配置失败");
        } else {
            // 未开启客服超时设置
            skillSetConfig.setReplyCsTimeout(-1);
            skillSetConfig.setReplyCsTimeoutTip(StringUtils.EMPTY);
            skillSetConfig.setCsDisconnTimeout(-1);
        }

        ServiceAssert.isTrue(StringUtils.isBlank(skillSetConfig.getCsNickname()) || (!skillSetConfig.getCsNickname().isEmpty() && skillSetConfig.getCsNickname().length() <= 30), StatusCode.Common.ILLEGAL_PARAM_ERROR.getCode(), "组内客服昵称长度在1-30之间");


        SkillSetConfig dbData = this.lambdaQuery().select(SkillSetConfig::getId)
                .eq(SkillSetConfig::getSkillSetId, skillSet.getId())
                .one();
        if (dbData == null) {
            this.save(skillSetConfig);
        } else {
            skillSetConfig.setId(dbData.getId());
            this.updateById(skillSetConfig);
            deleteCache(skillSet.getId());
        }
    }

    /**
     * 获取技能组配置
     *
     * @param skillSetId
     * @return
     */
    public SkillSetConfig queryBySkillSetId(Long skillSetId) {
        if (skillSetId == null) {
            return null;
        }

        String cacheKey = String.format(CacheKeyUtil.SkillSet.SKILL_SET_CONFIG, skillSetId);
        return Optional.ofNullable(redissonClient.getBucket(cacheKey).get())
                .map(o -> JsonUtil.toObject((String) o, SkillSetConfig.class))
                .orElseGet(() -> {
                    SkillSetConfig skillSetConfig = this.lambdaQuery().select().eq(SkillSetConfig::getSkillSetId, skillSetId).one();
                    if (skillSetConfig != null) {
                        redissonClient.getBucket(cacheKey).set(JsonUtil.toJson(skillSetConfig), Duration.of(CacheKeyUtil.EXPIRE_TIME_8H, ChronoUnit.MILLIS));
                    }
                    return skillSetConfig;
                });
    }

    /**
     * 获取技能组配置
     *
     * @param skillSetIdList
     * @return key:技能组id,value:技能组配置
     */
    public Map<Long, SkillSetConfig> queryBySkillSetId(List<Long> skillSetIdList) {
        if (CollectionUtils.isEmpty(skillSetIdList)) {
            return null;
        }

        Map<Long, SkillSetConfig> resultMap = new HashMap<>();

        List<Long> needQueryDbIdList = new ArrayList<>(skillSetIdList);

        RBatch redisBatch = redissonClient.createBatch();
        for (Long skillSetId : skillSetIdList) {
            String cacheKey = String.format(CacheKeyUtil.SkillSet.SKILL_SET_CONFIG, skillSetId);
            redisBatch.getBucket(cacheKey).getAsync();
        }
        BatchResult<?> batchResult = redisBatch.execute();
        Optional.ofNullable(batchResult.getResponses()).orElseGet(Lists::newArrayList)
                .stream()
                .filter(Objects::nonNull)
                .map(json -> JsonUtil.toObject(json.toString(), SkillSetConfig.class))
                .filter(Objects::nonNull)
                .forEach(skillSetConfig -> {
                    resultMap.put(skillSetConfig.getSkillSetId(), skillSetConfig);
                    needQueryDbIdList.remove(skillSetConfig.getSkillSetId());
                });

        if (CollectionUtils.isEmpty(needQueryDbIdList)) {
            return resultMap;
        }

        List<SkillSetConfig> skillSetConfigList = this.lambdaQuery().select().in(SkillSetConfig::getSkillSetId, needQueryDbIdList).list();
        if (CollectionUtils.isEmpty(skillSetConfigList)) {
            return resultMap;
        }

        redisBatch = redissonClient.createBatch();
        for (SkillSetConfig skillSetConfig : skillSetConfigList) {
            resultMap.put(skillSetConfig.getSkillSetId(), skillSetConfig);

            String cacheKey = String.format(CacheKeyUtil.SkillSet.SKILL_SET_CONFIG, skillSetConfig.getSkillSetId());
            redisBatch.getBucket(cacheKey).setAsync(JsonUtil.toJson(skillSetConfig), Duration.of(CacheKeyUtil.EXPIRE_TIME_8H, ChronoUnit.MILLIS));
        }

        redisBatch.execute();

        return resultMap;
    }

    /**
     * 获取技能组配置
     *
     * @param idList
     * @return
     */
    public Map<Long, SkillSetConfig> getSkillSetConfigMap(List<Long> idList) {
        if (CollectionUtils.isEmpty(idList)) {
            return Maps.newHashMap();
        }
        return this.lambdaQuery().select()
                .in(SkillSetConfig::getId, idList)
                .list().stream().collect(Collectors.toMap(SkillSetConfig::getId, v -> v, (o, n) -> o));
    }


    private void deleteCache(Long skillSetId) {
        String cacheKey = String.format(CacheKeyUtil.SkillSet.SKILL_SET_CONFIG, skillSetId);
        redissonClient.getBucket(cacheKey).deleteAsync();
    }
}
